package com.lyf.scm.plan.domain.service.impl;

import com.github.pagehelper.PageInfo;
import com.lyf.scm.common.enums.TableCodeEnum;
import com.lyf.scm.plan.api.dto.BaseSkuInfoDTO;
import com.lyf.scm.plan.domain.remote.basedata.dto.ChannelDTO;
import com.lyf.scm.plan.domain.remote.basedata.dto.ChannelInfoDTO;
import com.lyf.scm.plan.domain.remote.basedata.facade.BaseDataFacade;
import com.lyf.scm.plan.domain.remote.bigdata.dto.*;
import com.lyf.scm.plan.domain.remote.bigdata.facade.BlOlapFacade;
import com.lyf.scm.plan.domain.remote.bigdata.facade.CprfCoreFacade;
import com.lyf.scm.plan.domain.remote.item.dto.CategorySimpleDTO;
import com.lyf.scm.plan.domain.remote.item.dto.SkuInfoDTO;
import com.lyf.scm.plan.domain.remote.item.dto.SkuPriceDTO;
import com.lyf.scm.plan.domain.remote.item.facade.ItemCoreFacade;
import com.lyf.scm.plan.domain.remote.nateWork.dto.SearchSourceToCQueryDTO;
import com.lyf.scm.plan.domain.remote.nateWork.facade.NateWorkFacade;
import com.lyf.scm.plan.domain.remote.stock.dto.QueryRealTimeStockDTO;
import com.lyf.scm.plan.domain.remote.stock.dto.RealTimeStockDTO;
import com.lyf.scm.plan.domain.remote.stock.facade.StockCoreFacade;
import com.lyf.scm.plan.domain.service.CommonService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.lyf.scm.common.constants.CommonConstants.*;

/**
 * 常用方法
 *
 * @author wangchlei
 * @date 2020/10/30
 */
@Slf4j
@Service
public class CommonServiceImpl implements CommonService {
    @Resource
    private ItemCoreFacade itemCoreFacade;
    @Resource
    private BaseDataFacade baseDataFacade;
    @Resource
    private NateWorkFacade nateWorkFacade;
    @Resource
    private BlOlapFacade blOlapFacade;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private StockCoreFacade stockCoreFacade;
    @Resource
    private CprfCoreFacade cprfCoreFacade;


    /**
     * 根据skucodes获取sku相关的品类信息
     *
     * @param skuCodes
     * @return void
     **/
    @Override
    public Map<String, BaseSkuInfoDTO> getCategoryInfoBySkuCodes(List<String> skuCodes) {
        List<SkuInfoDTO> skuInfoDTOS = itemCoreFacade.skuListBySkuCodes(skuCodes);
        Map<String, SkuInfoDTO> skuCodeName = this.getSkuInfo(skuInfoDTOS);
        Map<Integer, List<CategorySimpleDTO>> categoryInfo = this.getCategoryInfo(skuInfoDTOS);
        BaseSkuInfoDTO baseSkuInfoDTO;
        SkuInfoDTO skuInfoDTO;
        List<CategorySimpleDTO> categorySimpleDTOS;
        Map<String, BaseSkuInfoDTO> map = new HashMap<>();
        for (String item : skuCodes) {
            baseSkuInfoDTO = new BaseSkuInfoDTO();
            baseSkuInfoDTO.setSkuCode(item);
            if (skuCodeName.get(item) != null) {
                skuInfoDTO = skuCodeName.get(item);
                baseSkuInfoDTO.setSkuName(skuInfoDTO.getName());
                categorySimpleDTOS = categoryInfo.get(skuInfoDTO.getCategoryId());
                if (!CollectionUtils.isEmpty(categorySimpleDTOS)) {
                    //排序 按照 大品类，中品类，小品类
                    categorySimpleDTOS.sort(Comparator.comparingInt(CategorySimpleDTO::getCategoryLevel));
                    BaseSkuInfoDTO finalBaseSkuInfoDTO = baseSkuInfoDTO;
                    categorySimpleDTOS.forEach(subItem -> this.setGeneralCategoryValue(subItem, finalBaseSkuInfoDTO));
                }
            }
            map.put(item, baseSkuInfoDTO);
        }
        return map;
    }

    /**
     * 设置通用属性 设置品类
     *
     * @param subItem
     * @return void
     **/
    private void setGeneralCategoryValue(CategorySimpleDTO subItem, BaseSkuInfoDTO baseSkuInfoDTO) {
        Integer level = subItem.getCategoryLevel();
        switch (level) {
            case 2:
                baseSkuInfoDTO.setPriCategoryCode(String.valueOf(subItem.getCategoryCode()));
                baseSkuInfoDTO.setCategory(subItem.getCategoryName());
                break;
            case 3:
                baseSkuInfoDTO.setSecCategoryCode(String.valueOf(subItem.getCategoryCode()));
                baseSkuInfoDTO.setCategory(baseSkuInfoDTO.getCategory() + "/" + subItem.getCategoryName());
                break;
            case 4:
                baseSkuInfoDTO.setTerCategoryCode(String.valueOf(subItem.getCategoryCode()));
                baseSkuInfoDTO.setCategory(baseSkuInfoDTO.getCategory() + "/" + subItem.getCategoryName());
                break;
            default:
        }
    }

    /**
     * 根据skuInfo获取sku品类信息
     *
     * @param skuInfoDTOS
     * @return void
     **/
    private Map<Integer, List<CategorySimpleDTO>> getCategoryInfo(List<SkuInfoDTO> skuInfoDTOS) {
        Map<Integer, List<CategorySimpleDTO>> categoryInfo = new HashMap<>();
        if (!CollectionUtils.isEmpty(skuInfoDTOS)) {
            List<CategorySimpleDTO> categorySimpleDTOS = itemCoreFacade.getAllCategoryByCategoryId(skuInfoDTOS.stream().map(SkuInfoDTO::getCategoryId).distinct().collect(Collectors.toList()));
            categoryInfo = categorySimpleDTOS.stream().collect(Collectors.toMap(CategorySimpleDTO::getId, CategorySimpleDTO::getParentCategoryDTOList));
        }
        return categoryInfo;
    }

    /**
     * 根据skuInfo获取sku-map信息
     *
     * @param skuInfoDTOS
     * @return void
     **/
    private Map<String, SkuInfoDTO> getSkuInfo(List<SkuInfoDTO> skuInfoDTOS) {
        //根据skuCodes查询skuInfo
        Map<String, SkuInfoDTO> skuCodeName = new HashMap<>();
        if (!CollectionUtils.isEmpty(skuInfoDTOS)) {
            skuCodeName = skuInfoDTOS.stream().collect(Collectors.toMap(SkuInfoDTO::getSkuCode, item -> item));
        }
        return skuCodeName;
    }

    /**
     * 获取sku平均价格，取100所有渠道组的价格，求平均值
     *
     * @param list
     * @return
     */
    @Override
    public Map<String, BigDecimal> getSkuAveragePrice(List<SkuPriceDTO> list) {
        Map<String, BigDecimal> result = new HashMap<>();
        try {
            // 先找到所有100的渠道组
            String redis_key_channe_list = "scm_sales_plan_redis_key_channel_list";
            List<ChannelDTO> channelList = (List<ChannelDTO>) redisTemplate.opsForValue().get(redis_key_channe_list);
            if (channelList == null) {
                ChannelInfoDTO channelQuery = new ChannelInfoDTO();
                channelQuery.setChannelType("100");
                channelQuery.setSaleUnitType(1);
                PageInfo<ChannelDTO> channelPage = baseDataFacade.getChannelPage(channelQuery);
                if (channelPage == null || channelPage.getList() == null || channelPage.getList().size() == 0) {
                    return result;
                }
                channelList = channelPage.getList();
                if (channelList == null || channelList.size() == 0) {
                    return result;
                } else {
                    redisTemplate.opsForValue().set(redis_key_channe_list, channelList, Duration.ofMinutes(15));
                }
            }
            // 把sku和渠道组合查询
            List<SkuPriceDTO> skuPriceQuery = new ArrayList<>();
            for (SkuPriceDTO skuPriceDTO : list) {
                for (ChannelDTO channelDTO : channelList) {
                    SkuPriceDTO dto = new SkuPriceDTO();
                    dto.setSkuCode(skuPriceDTO.getSkuCode());
                    dto.setSaleUnitCode(skuPriceDTO.getSaleUnitCode());
                    dto.setChannelCode(channelDTO.getChannelCode());
                    skuPriceQuery.add(dto);
                }
            }
            List<SkuPriceDTO> skuPriceDTOList = itemCoreFacade.getSkuPrice(skuPriceQuery);
            if (skuPriceDTOList == null || skuPriceDTOList.size() == 0) {
                return result;
            }
            // 按sku合并
            Map<String, List<SkuPriceDTO>> collect = new HashMap<>();
            for (SkuPriceDTO skuPriceDTO : skuPriceDTOList) {
                if (skuPriceDTO.getSalePrice() == null) {
                    continue;
                }
                String skuCode = skuPriceDTO.getSkuCode();
                List<SkuPriceDTO> subList = collect.get(skuCode);
                if (subList == null) {
                    subList = new ArrayList<>();
                    collect.put(skuCode, subList);
                }
                subList.add(skuPriceDTO);
            }
            // 把价格求平均值
            for (Map.Entry<String, List<SkuPriceDTO>> entry : collect.entrySet()) {
                String skuCode = entry.getKey();
                List<SkuPriceDTO> value = entry.getValue();
                if (value == null || value.size() == 0) {
                    continue;
                }
                BigDecimal sum = BigDecimal.ZERO;
                for (SkuPriceDTO skuPriceDTO : value) {
                    sum = sum.add(skuPriceDTO.getSalePrice());
                }
                result.put(skuCode, sum.divide(BigDecimal.valueOf(value.size()), 2, BigDecimal.ROUND_HALF_UP));
            }
            return result;
        } catch (Exception e) {
            log.error("取价格异常", e);
        }
        return result;
    }


    @Override
    public String getHighestPriorityWharehouseByChannelAndArea(String channel, String province, String city) {
        try {
            if (StringUtils.isEmpty(province)) {
                return null;
            }
            SearchSourceToCQueryDTO searchSourceToCQueryDTO = new SearchSourceToCQueryDTO();
            searchSourceToCQueryDTO.setChannelCode(channel);
            if (province.startsWith("320")) {
                if (StringUtils.isEmpty(city)) {
                    return null;
                }
                searchSourceToCQueryDTO.setCityCode(city);
            } else {
                searchSourceToCQueryDTO.setProvinceCode(province);
            }
            List<SearchSourceToCQueryDTO> list = nateWorkFacade.searchToCSource(Arrays.asList(searchSourceToCQueryDTO));
            if (list == null || list.size() == 0) {
                return null;
            } else if (list.size() == 1) {
                return list.get(0).getWarehouseCode();
            } else {
                List<SearchSourceToCQueryDTO> sortedList = list.stream().sorted(
                        Comparator.comparing(SearchSourceToCQueryDTO::getPority).reversed()).collect(Collectors.toList());
                return sortedList.get(0).getWarehouseCode();
            }
        } catch (Exception e) {
            log.error("从组网取发货仓失败:{},{},{}", channel, province, city, e);
            return null;
        }
    }

    @Override
    public OlapQueryResultDTO queryMultiDimensional(OlapMetadataDTO olapMetadataDTO) {
        OlapQueryMetadataDTO param = new OlapQueryMetadataDTO();
        List<Map<String, Object>> aggregation = new ArrayList<>();
        Map<String, Object> aggregationMap = new HashMap<>();
        aggregationMap.put("function", "SUM");
        aggregationMap.put("columnList", new ArrayList<>(Arrays.asList("JC_SALE_QUANTITY")));
        aggregation.add(aggregationMap);
        param.setAggregation(aggregation);
        param.setDimensions(olapMetadataDTO.getDimensions());
        param.setStartTime(olapMetadataDTO.getStartTime());
        param.setEndTime(olapMetadataDTO.getEndTime());
        List<OlapQueryFilterDTO> filter = new ArrayList<>();
        List<Map<String, List<String>>> filterList = olapMetadataDTO.getFilter();
        for (Map<String, List<String>> map : filterList) {
            for (String key : map.keySet()) {
                OlapQueryFilterDTO filterDto = new OlapQueryFilterDTO();
                filterDto.setColumn(key);
                List<OlapQueryFilterOperatorDTO> operatorList = new ArrayList<>();
                OlapQueryFilterOperatorDTO operator = new OlapQueryFilterOperatorDTO();
                operator.setOperator("in");
                operator.setValueList(map.get(key));
                operatorList.add(operator);
                filterDto.setOperatorList(operatorList);
                filter.add(filterDto);
            }
        }
        param.setFilter(filter);
        OlapQueryResultDTO olapQueryResultDTO = blOlapFacade.queryMultiDimensional(param);
        return olapQueryResultDTO;
    }

    /**
     * 设置缓存日志信息
     *
     * @param queryCache
     * @param dataCache
     * @param tableCode
     * @return void
     **/
    @Override
    public void setCacheRecord(String queryCache, String dataCache, String tableCode) {
        if (TableCodeEnum.DIRECT_FORECAST.getCode().equals(tableCode)) {
            redisTemplate.opsForSet().add(REDIS_CACHE_DIRECT_FORECAST, queryCache);
            redisTemplate.opsForSet().add(REDIS_CACHE_DIRECT_FORECAST, dataCache);
            redisTemplate.expire(REDIS_CACHE_DIRECT_FORECAST, 6, TimeUnit.HOURS);
        }
        if (TableCodeEnum.OTHER_PLAN.getCode().equals(tableCode)) {
            redisTemplate.opsForSet().add(REDIS_CACHE_OTHER_CHANNEL, queryCache);
            redisTemplate.opsForSet().add(REDIS_CACHE_OTHER_CHANNEL, dataCache);
            redisTemplate.expire(REDIS_CACHE_OTHER_CHANNEL, 6, TimeUnit.HOURS);
        }
        if (TableCodeEnum.REQUIREMENT_PLAN.getCode().equals(tableCode)) {
            redisTemplate.opsForSet().add(REDIS_CACHE_REQUIREMENT, queryCache);
            redisTemplate.opsForSet().add(REDIS_CACHE_REQUIREMENT, dataCache);
            redisTemplate.expire(REDIS_CACHE_REQUIREMENT, 6, TimeUnit.HOURS);
        }
        if (TableCodeEnum.SALE_PLAN.getCode().equals(tableCode)) {
            redisTemplate.opsForSet().add(REDIS_CACHE_SALE_PLAN, queryCache);
            redisTemplate.opsForSet().add(REDIS_CACHE_SALE_PLAN, dataCache);
            redisTemplate.expire(REDIS_CACHE_SALE_PLAN, 6, TimeUnit.HOURS);
        }

    }

    /**
     * 重置缓存日志时长
     *
     * @param tableCode
     * @return void
     **/
    @Override
    public void reSetCacheRecord(String tableCode) {
        if (TableCodeEnum.DIRECT_FORECAST.getCode().equals(tableCode)) {
            redisTemplate.expire(REDIS_CACHE_DIRECT_FORECAST, 6, TimeUnit.HOURS);
        }
        if (TableCodeEnum.OTHER_PLAN.getCode().equals(tableCode)) {
            redisTemplate.expire(REDIS_CACHE_OTHER_CHANNEL, 6, TimeUnit.HOURS);
        }
        if (TableCodeEnum.REQUIREMENT_PLAN.getCode().equals(tableCode)) {
            redisTemplate.expire(REDIS_CACHE_REQUIREMENT, 6, TimeUnit.HOURS);
        }
        if (TableCodeEnum.SALE_PLAN.getCode().equals(tableCode)) {
            redisTemplate.expire(REDIS_CACHE_SALE_PLAN, 6, TimeUnit.HOURS);
        }

    }

    @Override
    public void clearCache(String tableCode) {
        //清空缓存
        Set<String> keys = redisTemplate.opsForSet().members(tableCode);
        for (String item : keys) {
            if (exists(item)) {
                redisTemplate.delete(item);
            }
        }
    }

    @Override
    public List<RealTimeStockDTO> listAvailableQty(QueryRealTimeStockDTO queryData) {
        return stockCoreFacade.listAvailableQty(queryData);
    }

    @Override
    public List<TiSsStockResultDTO> thirteenWeekPredictSale(TiSsStockQueryDTO query) {
        return cprfCoreFacade.thirteenWeekPredictSale(query);
    }

    private boolean exists(String key) {
        return redisTemplate.hasKey(key);
    }
}
